package record;

import metadata.TableInfo;
import buffer.Buffer;
import buffer.BufferManger;
import jminidb.JMiniDB;
import io.Block;
import io.Page;

/**
 * Manages the placement and access of records on a disk block.
 * 
 * @author NIL
 */

public class RecordPage {
	public static final int EMPTY = 0, USED = 1;

	private Block blk;
	private TableInfo ti;
	// private Transaction tx;
	private int slotsize;
	private int currentslot = -1;
	private BufferManger bm;

	/**
	 * Creates the record manager for the specified block. The current record is
	 * set to be before the first one.
	 * 
	 * @param blk
	 *            a reference to the disk block
	 * @param ti
	 *            the table's metadata
	 * @param tx
	 *            the transaction performing the operations
	 */
	public RecordPage(Block blk, TableInfo ti/* , Transaction tx */) {
		// this.tx = tx;
		// tx.pin(blk);

		this.blk = blk;
		this.ti = ti;
		slotsize = ti.getRecordLength() + Page.INT_SIZE;
		bm = JMiniDB.getBufferMrg();
	}

	/**
	 * Closes the manager, by unpinning the block.
	 */
	public void close() {
		// if (blk != null) tx.unpin(blk);

		blk = null;
	}

	/**
	 * Moves to the next record in the block. Returns false if there is no next
	 * record.
	 * 
	 * @return false if there is no next record.
	 */
	public boolean next() {
		return searchFor(USED);
	}

	/**
	 * Returns the integer value stored for the specified field of the current
	 * record.
	 * 
	 * @param fldname
	 *            the name of the field.
	 * @return the integer stored in that field
	 */
	public int getInt(String fldname) {
		int position = fieldpos(fldname);
		Buffer bf = bm.allocate(blk);
		int rst = bf.getInt(position);
		bm.free(bf);
		return rst;

		// return tx.getInt(blk, position);
	}

	/**
	 * Returns the string value stored for the specified field of the current
	 * record.
	 * 
	 * @param fldname
	 *            the name of the field.
	 * @return the string stored in that field
	 */
	public String getString(String fldname) {
		int position = fieldpos(fldname);
		// return tx.getString(blk, position);
		Buffer bf = bm.allocate(blk);
		String rst = bf.getString(position);
		bm.free(bf);
		return rst;
	}

	/**
	 * Stores an integer at the specified field of the current record.
	 * 
	 * @param fldname
	 *            the name of the field
	 * @param val
	 *            the integer value stored in that field
	 */
	public void setInt(String fldname, int val) {
		int position = fieldpos(fldname);
		Buffer bf = bm.allocate(blk);
		bf.setInt(position, val);
		bm.free(bf);
		// tx.setInt(blk, position, val);
	}

	/**
	 * Stores a string at the specified field of the current record.
	 * 
	 * @param fldname
	 *            the name of the field
	 * @param val
	 *            the string value stored in that field
	 */
	public void setString(String fldname, String val) {
		int position = fieldpos(fldname);
		Buffer bf = bm.allocate(blk);
		bf.setString(position, val);
		bm.free(bf);
	}

	/**
	 * Deletes the current record. Deletion is performed by just marking the
	 * record as "deleted"; the current record does not change. To get to the
	 * next record, call next().
	 */
	public void delete() {
		int position = currentpos();
		Buffer bf = bm.allocate(blk);
		bf.setInt(position, EMPTY);
		bm.free(bf);
	}

	/**
	 * Inserts a new, blank record somewhere in the page. Return false if there
	 * were no available slots.
	 * 
	 * @return false if the insertion was not possible
	 */
	public boolean insert() {
		currentslot = -1;
		boolean found = searchFor(EMPTY);
		if (found) {
			int position = currentpos();
			// tx.setInt(blk, position, USED);
			Buffer bf = bm.allocate(blk);
			bf.setInt(position, USED);
			bm.free(bf);
		}
		return found;
	}

	/**
	 * Sets the current record to be the record having the specified ID.
	 * 
	 * @param id
	 *            the ID of the record within the page.
	 */
	public void moveToId(int id) {
		currentslot = id;
	}

	/**
	 * Returns the ID of the current record.
	 * 
	 * @return the ID of the current record
	 */
	public int currentId() {
		return currentslot;
	}

	private int currentpos() {
		return currentslot * slotsize;
	}

	private int fieldpos(String fldname) {
		int offset = Page.INT_SIZE + ti.offset(fldname);
		return currentpos() + offset;
	}

	private boolean isValidSlot() {
		return currentpos() + slotsize <= Page.BLOCK_SIZE;
	}

	private boolean searchFor(int flag) {
		boolean rst = false;
		Buffer bf = bm.allocate(blk);

		currentslot++;
		while (isValidSlot()) {
			int position = currentpos();
			if (bf.getInt(position) == flag) {
				rst = true;
				break;
			}

			currentslot++;
		}
		bm.free(bf);
		return rst;
	}

	public Double getFloat(String fldname) {
		int position = fieldpos(fldname);
		Buffer bf = bm.allocate(blk);
		double rst = bf.getFloat(position);
		bm.free(bf);
		return rst;
	}

	public void setFloat(String fldname, Double val) {
		int position = fieldpos(fldname);
		Buffer bf = bm.allocate(blk);
		bf.setFloat(position, val);
		bm.free(bf);
	}
}
